package com.utry.netty.websocket;

import android.util.Log;

import com.utry.netty.client.ConnectStocktakingListener;
import com.utry.netty.intf.ConnectClientCallback;
import com.utry.netty.intf.MessageClientCallback;

import java.net.URI;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;

public class NettyStocktakingClient {

    private static final String TAG = NettyStocktakingClient.class.getSimpleName();
    private int PORT = 0;
    private String IP = "";
    private static NettyStocktakingClient nettyClient;
    private Channel channel;
    private ChannelFuture channelFuture;
    private MessageClientCallback messageClientCallback;
    private ConnectClientCallback connectCallback;
    private MockClientHandler webSocketClientHandler;

    public static NettyStocktakingClient getInstance() {
        if (nettyClient == null) {
            nettyClient = new NettyStocktakingClient();
        }
        return nettyClient;
    }

    /**
     * 设置Ip和端口号
     *
     * @param ip
     * @param port
     */
    public void setIPAndPort(String ip, int port) {
        this.IP = ip;
        this.PORT = port;
    }

    /**
     * 需要在子线程中发起连接
     */
    public void connectNetty() {
        new Thread(() -> connect()).start();
    }

    /**
     * 获取与服务端的连接
     */
    public static Channel getChannel() {
        if (nettyClient == null) {
            return null;
        }
        return nettyClient.channel;
    }

    private NioEventLoopGroup group;
    private Bootstrap bootstrap;

    /**
     * 连接服务端
     */
    private void connect() {
        try {
            //netty基本操作，线程组
            EventLoopGroup group = new NioEventLoopGroup();
            //netty基本操作，启动类
            Bootstrap boot = new Bootstrap();
            boot.option(ChannelOption.SO_KEEPALIVE, true)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .group(group)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(new IdleStateHandler(10, 0, 2));
                            pipeline.addLast("http-codec", new HttpClientCodec());
                            pipeline.addLast(new LoggingHandler(LogLevel.INFO));
                            pipeline.addLast("aggregator", new HttpObjectAggregator(1024 * 1024 * 10));
                            pipeline.addLast("hookedHandler", new MockClientHandler(NettyStocktakingClient.this, connectCallback));

                        }
                    });
            //websocke连接的地址，/hello是因为在服务端的websockethandler设置的
            URI webSocketURI = new URI("ws://192.168.10.74:18085");
            HttpHeaders httpHeaders = new DefaultHttpHeaders();
            //进行握手
            WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(webSocketURI, WebSocketVersion.V13, (String) null, true, httpHeaders);
            //客户端与服务端连接的通道，final修饰表示只会有一个
            channel = boot.connect(webSocketURI.getHost(), webSocketURI.getPort()).sync().channel();
            webSocketClientHandler = (MockClientHandler) channel.pipeline().get("hookedHandler");
            webSocketClientHandler.setHandshaker(handshaker);
            handshaker.handshake(channel);
            //阻塞等待是否握手成功
            webSocketClientHandler.handshakeFuture().sync();
            Log.v(TAG, "握手成功");
            //给服务端发送的内容，如果客户端与服务端连接成功后，可以多次掉用这个方法发送消息

        } catch (
                Exception e) {
            if (null != connectCallback) {
                connectCallback.onConnect(1, "连接失败");
            }
            Log.e(TAG, "连接失败：" + e.getMessage());
            e.printStackTrace();
        }

    }

    public void disConnect() {
        if (channelFuture != null && channelFuture.channel() != null) {
            channelFuture.channel().close();
        }
    }

    public void destroy() {
        if (channelFuture != null && channelFuture.channel() != null) {
            channelFuture.channel().close();
            channelFuture.channel().eventLoop().shutdownGracefully();
        }
    }

    public void setMessageClientCallback(MessageClientCallback messageClientCallback) {
        this.messageClientCallback = messageClientCallback;
        if (null != webSocketClientHandler) {
            webSocketClientHandler.setMessageClientCallback(messageClientCallback);
        }
    }

    public void setConnectClientCallback(ConnectClientCallback connectCallback) {
        this.connectCallback = connectCallback;
    }
}
